﻿using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using CalcSharp.Core.Containers;
using ICSharpCode.SharpZipLib.Zip;

namespace CalcSharp.Core.Memory
{
    public class MemoryDumpWriter
    {
        private ZipOutputStream zo;
        private MemoryDumpInfo dmp;
        private FileStream output;
        private MemoryStream serialized;

        private int clevel;

        public int CompressionLevel
        {
            get { return clevel; }
            set
            {
                if (value > 9) clevel = 9;
                else if (value < 0) clevel = 0;
                else clevel = value;
            }
        }

        public MemoryDumpWriter(string Filepath)
        {
            if (string.IsNullOrEmpty(Filepath)) throw new ArgumentException("Filepath can't be null");
            this.clevel = 0;
            this.output = File.Create(Filepath);
            this.zo = new ZipOutputStream(this.output);
            zo.UseZip64 = UseZip64.On;
            this.dmp = new MemoryDumpInfo();
            this.serialized = new MemoryStream();
        }

        private void ResetSerialized()
        {
            if (serialized != null)
            {
                this.serialized.Close();
                this.serialized = null;
                this.serialized = new MemoryStream();
            }
            else this.serialized = new MemoryStream();
        }

        public void SerializeAll(ref FloatManager fm, ref ComplexManager cm, ref MatrixManager mm, ref SetManager sm, ref StringManager stm, ref FunctionManager fncm)
        {
            zo.SetLevel(clevel);
            ZipEntry tmp;
            BinaryFormatter bf = new BinaryFormatter();
            byte[] buffer = new byte[4096];
            Data Fd;
            CplxData Cd;
            MatrixData Md;
            SetData Sd;
            StringData Std;
            FunctData Fncd;
            int buff;
            this.dmp.MatrixStorage = mm.StorageType;
            this.dmp.SetStorage = sm.StorageMode;

            tmp = new ZipEntry("dump.bin");
            tmp.DateTime = DateTime.Now;
            bf.Serialize(this.serialized, this.dmp);
            this.serialized.Seek(0, SeekOrigin.Begin);
            zo.PutNextEntry(tmp);
            do
            {
                buff = serialized.Read(buffer, 0, buffer.Length);
                zo.Write(buffer, 0, buff);
            }
            while (buff > 0);

            foreach (string n in fm.keys)
            {
                Fd = fm.PackSingle(n);
                ResetSerialized();
                bf.Serialize(this.serialized, Fd);
                tmp = new ZipEntry("float/" + n + ".bin");
                tmp.DateTime = DateTime.Now;
                zo.PutNextEntry(tmp);
                this.serialized.Seek(0, SeekOrigin.Begin);
                do
                {
                    buff = serialized.Read(buffer, 0, buffer.Length);
                    zo.Write(buffer, 0, buff);
                }
                while (buff > 0);
            }

            foreach (string n in cm.keys)
            {
                Cd = cm.PackSingle(n);
                ResetSerialized();
                bf.Serialize(this.serialized, Cd);
                tmp = new ZipEntry("cplx/" + n + ".bin");
                tmp.DateTime = DateTime.Now;
                zo.PutNextEntry(tmp);
                this.serialized.Seek(0, SeekOrigin.Begin);
                do
                {
                    buff = serialized.Read(buffer, 0, buffer.Length);
                    zo.Write(buffer, 0, buff);
                }
                while (buff > 0);
            }

            foreach (string n in mm.keys)
            {
                Md = mm.PackSingle(n);
                ResetSerialized();
                bf.Serialize(this.serialized, Md);
                tmp = new ZipEntry("mtrx/" + n + ".bin");
                tmp.DateTime = DateTime.Now;
                zo.PutNextEntry(tmp);
                this.serialized.Seek(0, SeekOrigin.Begin);
                do
                {
                    buff = serialized.Read(buffer, 0, buffer.Length);
                    zo.Write(buffer, 0, buff);
                }
                while (buff > 0);
            }

            foreach (string n in sm.keys)
            {
                Sd = sm.PackSingle(n);
                ResetSerialized();
                bf.Serialize(this.serialized, Sd);
                tmp = new ZipEntry("set/" + n + ".bin");
                tmp.DateTime = DateTime.Now;
                zo.PutNextEntry(tmp);
                this.serialized.Seek(0, SeekOrigin.Begin);
                do
                {
                    buff = serialized.Read(buffer, 0, buffer.Length);
                    zo.Write(buffer, 0, buff);
                }
                while (buff > 0);
            }

            foreach (string n in stm.keys)
            {
                Std = stm.PackSingle(n);
                ResetSerialized();
                bf.Serialize(this.serialized, Std);
                tmp = new ZipEntry("string/" + n + ".bin");
                tmp.DateTime = DateTime.Now;
                zo.PutNextEntry(tmp);
                this.serialized.Seek(0, SeekOrigin.Begin);
                do
                {
                    buff = serialized.Read(buffer, 0, buffer.Length);
                    zo.Write(buffer, 0, buff);
                }
                while (buff > 0);
            }

            foreach (string n in fncm.keys)
            {
                Fncd = fncm.PackSingle(n);
                ResetSerialized();
                bf.Serialize(this.serialized, Fncd);
                tmp = new ZipEntry("function/" + n + ".bin");
                tmp.DateTime = DateTime.Now;
                zo.PutNextEntry(tmp);
                this.serialized.Seek(0, SeekOrigin.Begin);
                do
                {
                    buff = serialized.Read(buffer, 0, buffer.Length);
                    zo.Write(buffer, 0, buff);
                }
                while (buff > 0);
            }

            zo.Finish();
            zo.Close();
        }

    }
}
